Optional
Optional<T> est un conteneur introduit en Java 8 qui représente une valeur qui peut être présente ou absente. Il remplace l'usage de null pour signaler l'absence de résultat, et force le code appelant à gérer explicitement ce cas.
Pourquoi Optional?
Sans Optional, retourner null crée des NullPointerException silencieux :
// DANGEREUX — peut lancer NullPointerException
public Etudiant trouverParNom(String nom) {
// ... retourne null si pas trouvé
}
Etudiant e = trouverParNom("Alice");
System.out.println(e.getNote()); // NullPointerException si null
Avec Optional, l'absence de valeur est explicite dans le type de retour :
public Optional<Etudiant> trouverParNom(String nom) {
// ... retourne Optional.empty() si pas trouvé
}
Créer un Optional
// Valeur présente — on est certain que la valeur n'est pas null
Optional<String> avecValeur = Optional.of("Alice");
// Valeur absente — on sait qu'il n'y a rien
Optional<String> vide = Optional.empty();
// Valeur qui pourrait être null — on n'est pas certain
Optional<String> peutEtreNull = Optional.ofNullable(null); // produit un Optional vide
Optional<String> peutEtreNull2 = Optional.ofNullable("Bob"); // produit Optional avec "Bob"
of vs ofNullable — quelle différence ?
Imaginons une méthode qui cherche un étudiant et peut retourner null si non trouvé :
String nom = trouverNom(); // peut retourner null
// of(null) — plante immédiatement avec NullPointerException
Optional<String> opt1 = Optional.of(nom); // DANGER si nom est null
// ofNullable(null) — produit un Optional vide, sans exception
Optional<String> opt2 = Optional.ofNullable(nom); // sûr, gère le null
La règle est simple :
Optional.of(v)→ utiliser seulement si on est certain quevn'est pasnullOptional.ofNullable(v)→ utiliser quandvpourrait êtrenull
Vérifier et extraire la valeur
Optional<String> opt = Optional.of("Alice");
// Vérifier si une valeur est présente
if (opt.isPresent()) {
System.out.println(opt.get()); // Alice
}
// Raccourci : ifPresent() avec une lambda
opt.ifPresent(v -> System.out.println(v));
opt.ifPresent(System.out::println); // identique avec référence de méthode
get()sur unOptionalvide lanceNoSuchElementException. Toujours vérifier avecisPresent()avant.
Valeur par défaut
orElse — valeur fixe de remplacement
Si l'Optional est vide, retourne la valeur passée en argument. Si l'Optional contient quelque chose, retourne cette valeur.
Optional<String> avecValeur = Optional.of("Alice");
Optional<String> vide = Optional.empty();
System.out.println(avecValeur.orElse("inconnu")); // Alice ← valeur présente, on l'utilise
System.out.println(vide.orElse("inconnu")); // inconnu ← vide, on utilise le défaut
orElseGet — valeur calculée à la demande
Même idée qu'orElse, mais au lieu de passer une valeur fixe, on passe un Supplier — une lambda sans argument qui calcule la valeur de remplacement. Cette lambda n'est exécutée que si l'Optional est vide.
Optional<String> vide = Optional.empty();
// La lambda () -> ... n'est appelée que si vide est... vide
String resultat = vide.orElseGet(() -> "valeur calculée");
System.out.println(resultat); // valeur calculée
C'est utile quand produire la valeur par défaut est coûteux (requête BD, calcul long) — on évite de le faire si ce n'est pas nécessaire.
orElseThrow — erreur explicite si absent
Si l'Optional est vide, lance l'exception fournie. Force le code appelant à traiter le cas d'absence comme une situation anormale.
Optional<String> vide = Optional.empty();
// Lance IllegalStateException si vide
String resultat = vide.orElseThrow(() -> new IllegalStateException("Étudiant non trouvé"));
Optional<String> avecValeur = Optional.of("Alice");
// Aucune exception : retourne "Alice"
String nom = avecValeur.orElseThrow(() -> new IllegalStateException("Étudiant non trouvé"));
System.out.println(nom); // Alice
Utilisation avec les Streams
findFirst() et findAny() retournent un Optional car il est possible qu'aucun élément ne corresponde :
import java.util.List;
import java.util.Optional;
public class OptionalStream {
public static void main(String[] args) {
List<String> noms = List.of("Alice", "Bob", "Clara");
// Trouver le premier nom qui commence par "C"
Optional<String> trouve = noms.stream()
.filter(n -> n.startsWith("C"))
.findFirst();
// Option 1 : valeur par défaut
System.out.println(trouve.orElse("aucun")); // Clara
// Option 2 : traitement conditionnel
trouve.ifPresent(n -> System.out.println("Trouvé : " + n));
// Cas où rien n'est trouvé
Optional<String> absent = noms.stream()
.filter(n -> n.startsWith("Z"))
.findFirst();
System.out.println(absent.orElse("aucun")); // aucun
}
}
Résumé des méthodes clés
| Méthode | Description |
|---|---|
Optional.of(v) | Crée un Optional avec une valeur non-null |
Optional.empty() | Crée un Optional vide |
Optional.ofNullable(v) | Crée un Optional, vide si v est null |
isPresent() | true si une valeur est présente |
get() | Retourne la valeur (exception si vide) |
orElse(défaut) | Retourne la valeur ou le défaut |
orElseGet(supplier) | Retourne la valeur ou calcule un défaut |
orElseThrow(supplier) | Retourne la valeur ou lance une exception |
ifPresent(consumer) | Exécute une action si valeur présente |